# TODO: 导入必要的库和模块
import numpy as np  # 用于处理数组和数值计算
from sklearn import datasets  # 用于加载内置数据集
from sklearn.model_selection import train_test_split  # 用于划分数据集
from sklearn.neighbors import KNeighborsClassifier  # KNN分类器
from sklearn.metrics import accuracy_score  # 用于计算准确率
import matplotlib.pyplot as plt  # 用于数据可视化
from tqdm import tqdm  # 用于显示循环进度条
from matplotlib.backends.backend_pdf import PdfPages  # 用于将图形保存为PDF文件
import pickle  # 用于模型的序列化和存储
from matplotlib import rcParams  # 用于设置Matplotlib参数

# 设置字体为 SimHei，以便在图形中正确显示中文
rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体为黑体
rcParams['axes.unicode_minus'] = False  # 解决负号'-'显示为方块的问题

# TODO: 加载数字数据集
digits = datasets.load_digits()  # 从sklearn中加载手写数字数据集
X = digits.data  # 特征数据，即784个像素值
y = digits.target  # 标签数据，即对应的数字（0-9）

# TODO: 将数据集划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)  # 将数据集划分为80%的训练集和20%的测试集

# TODO: 初始化变量以存储最佳准确率，相应的k值和最佳knn模型
best_accuracy = 0.0  # 最佳准确率初始值
best_k = 0  # 最佳k值初始值
best_knn_model = None  # 最佳KNN模型初始值

# TODO: 初始化一个列表以存储每个k值的准确率
k_values = []  # 用于记录k值
accuracies = []  # 用于记录对应的准确率

# TODO: 尝试从1到40的k值，对于每个k值，训练knn模型，保存最佳准确率，k值和knn模型
with tqdm(range(1, 41), disable=False) as t:  # 用tqdm显示进度条
    for k in t:  # 循环遍历k值从1到40
        knn = KNeighborsClassifier(n_neighbors=k)  # 创建KNN分类器实例，并设定k值
        knn.fit(X_train, y_train)  # 在训练集上训练KNN模型
        y_pred = knn.predict(X_test)  # 在测试集上进行预测
        accuracy = accuracy_score(y_test, y_pred)  # 计算预测的准确率
        k_values.append(k)  # 将当前k值添加到k_values列表
        accuracies.append(accuracy)  # 将当前准确率添加到accuracies列表

        # 更新最佳模型
        if accuracy > best_accuracy:  # 如果当前准确率更高，则更新最佳值
            best_accuracy = accuracy  # 更新最佳准确率
            best_k = k  # 更新最佳k值
            best_knn_model = knn  # 更新最佳KNN模型
        t.set_description(f"K={k}, Accuracy={accuracy:.2f}")  # 更新进度条描述

    # TODO: 绘制k值与准确率的图表
    plt.figure(figsize=(10, 6))  # 设置图形大小
    plt.plot(k_values, accuracies, marker='o', linestyle='-')  # 绘制k值与准确率的关系图
    plt.xlabel('K值')  # x轴标签
    plt.ylabel('准确率')  # y轴标签
    plt.title('K值与准确率的关系')  # 图表标题
    plt.axvline(x=best_k, color='r', linestyle='--')  # 绘制竖直线标示最佳k值
    plt.annotate(f'最优K值: {best_k}, 准确率: {best_accuracy:.2f}',
                 xy=(best_k, best_accuracy), xytext=(best_k + 1, best_accuracy - 0.02))  # 注释最佳k值和准确率

# TODO: 将图表保存为PDF文件
with PdfPages('accuracy_plot.pdf') as pdf:  # 创建一个PdfPages对象以保存多个图形
    pdf.savefig(plt.gcf())  # 保存当前图形到PDF文件

# TODO: 将最佳KNN模型保存到二进制文件
with open('best_knn_model.pkl', 'wb') as f:  # 以二进制写入模式打开文件
    pickle.dump(best_knn_model, f)  # 将模型存储到文件

# TODO: 打印最佳准确率和相应的k值
print(f"最佳准确率为: {best_accuracy:.2f}, 对应的K值为: {best_k}")  # 输出最佳结果

# 关闭matplotlib图表显示（如果你不需要在屏幕上查看图表）
plt.close()  # 关闭当前图形